home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / ibmbios.aqm / ibmbios.asm
Assembly Source File  |  1984-10-14  |  43KB  |  1,189 lines

  1.    PAGE   60,132
  2.  
  3. ;        Title:            IBM-DOS Basic Input/Output System
  4.  
  5. ;        Name:             IBMBIO.COM
  6.  
  7. ;        Purpose: Hardware dependent routines that form the Basic
  8. ;                          Input/Output System (BIOS) for MS-DOS on the IBM
  9. ;                          Personal Computer.
  10.  
  11. ;        Revision:         January 1, 1983 2:00 PM
  12.  
  13. DATA     SEGMENT AT 00H
  14.  
  15.          ORG      006CH
  16.  
  17. KYBD_BRK_VECTOR   DW       ?
  18.  
  19.          ORG      400H
  20.  
  21. RS232_BASE        DW       4 DUP (?)         ; Addresses of RS-232 Adapters
  22. PRINTER_BASE      DW       4 DUP (?)         ; Addresses of printers
  23. EQUIP_FLAG        DW       ?                 ; Installed hardware
  24. MFG_TST           DB       ?                 ; Initialization Flag
  25. MEMORY_SIZE       DW       ?                 ; Memory size in K bytes
  26. IO_RAM_SIZE       DW       ?                 ; Memory in I/O channel
  27.  
  28. ; ----- Keyboard Data Areas
  29.  
  30. KB_FLAG           DB       ?                 ; Shift state bits
  31. KB_FLAG_1         DB       ?                 ; Second byte of keyboard status
  32. ALT_INPUT         DB       ?                 ; Storage for alternate keypad entry
  33. BUFFER_HEAD       DW       ?                 ; Pointer to head of keyboard buffer
  34. BUFFER_TAIL       DW       ?                 ; Pointer to tail of keyboard buffer
  35. KB_BUFFER         DW       16 DUP (?)        ; Room for 15 entries
  36. KB_BUFFER_END     LABEL    WORD
  37.  
  38.          ORG      500H
  39.  
  40. STATUS_BYTE       DB       ?                 ; Set to 0 by INIT
  41.                   DB       3 DUP (?)
  42. ONE_DRIVE_IO      DB       ?                 ; Used in single drive copies
  43.  
  44. DATA     ENDS
  45.  
  46.  
  47.  
  48.  
  49. DOSSEG   SEGMENT  AT 0BFH
  50.  
  51. DOSSEG_FAR        LABEL    FAR
  52.  
  53. DOSSEG   ENDS
  54.  
  55.  
  56. CODE     SEGMENT  PUBLIC 'CODE'
  57.          ASSUME CS:CODE,SS:CODE,ES:DATA,DS:DATA
  58.          
  59.  
  60. BIOS:
  61.  
  62. ;        The following jump table provides MS-DOS with access to the 
  63. ;          respective hardware dependent driver routines.
  64.  
  65.          JMP      INIT              ; System initialization
  66.          JMP      STATUS            ; Console Status Check
  67.          JMP      CONIN             ; Console Input
  68.          JMP      CONOUT            ; Console Output
  69.          JMP      PRINT             ; Printer Output
  70.          JMP      AUXIN             ; Auxiliary Input
  71.          JMP      AUXOUT            ; Auxiliary Output
  72.          JMP      READ              ; Disk Read
  73.          JMP      WRITE             ; Disk Write
  74.          JMP      DSKCHG            ; Return Disk Change Status
  75.          JMP      SETDATE           ; Set Current Date
  76.          JMP      SETTIME           ; Set Current Time
  77.          JMP      GETDATE           ; Read Time and Date
  78.          JMP      FLUSH             ; Flush Keyboard Input Buffer
  79.          JMP      MAPDEV            ; Device mapping
  80.  
  81. PAPER_OUT_MSG:
  82.          DB       13,10,'Out of paper',13,10,00
  83.  
  84. PRINTER_FAULT_MSG:
  85.          DB       13,10,'Printer fault',13,10,00
  86.  
  87. AUX_ERROR_MSG:
  88.          DB       13,10,'Aux I/O error',13,10,00
  89.  
  90.  
  91. ;        Procedure:        STATUS
  92.  
  93. ;        Purpose: Return status of console input
  94.  
  95. ;        Entry:            No significant registers
  96.  
  97. ;        Exit:             If char is ready:
  98. ;                                   Zero Flag is cleared
  99. ;                                   [AL] = Character
  100. ;                          If no char is ready:
  101. ;                                   Zero Flag is set
  102.  
  103. ;        Once character is returned with this call, the same character is
  104. ;          returned every time the call is made until a CONIN call is made.
  105. ;        In other words, this call leaves the character in the input buffer,
  106. ;          and only CONIN can remove it.
  107. ;        No registers other than [AL] are (or may be) changed.
  108.  
  109. STATUS_FAR        PROC     FAR
  110. STATUS   LABEL    NEAR
  111.  
  112.          MOV      AL,CS:BYTE PTR CHAR_BUFFER ; Check for a character
  113.          OR       AL,AL             ; Character available ?
  114.          JNZ      S2                ; Yes, exit - character in [AL] Zero Flag set
  115.          PUSH     DX                ; Save [DX] on stack for later restoration
  116.          XCHG     DX,AX             ; Save [AX] in [DX]
  117.          MOV      AH,01             ; Prep for Keyboard I/O
  118.          INT      16H               ; (See page A-23 of IBM Tech Ref Manual)
  119.          JZ       S1                ; Zero Flag indicates char is available
  120.          CMP      AX,7200H ; Do we need to replace char from I/O handler ?
  121.          JNZ      S1                ; No, continue
  122.          MOV      AL,10H            ; Yes, substitute a Control-P
  123.          OR       AL,AL             ; Set flags for Control-P
  124. S1:
  125.          MOV      AH,DH             ; Restore [AH] from prior save to [DH]
  126.          POP      DX                ; Restore [DX] off stack
  127. S2:
  128.          RET
  129.  
  130. STATUS_FAR        ENDP
  131.  
  132. ;        Procedure:        KEYBRK
  133.  
  134. ;        Purpose: New Keyboard Break Code handler.  INIT points
  135. ;                          Get Control on Keyboard Break (1BH) to this code.
  136.  
  137. ;        Entry:            On INT 1BH
  138.  
  139. ;        Exit:             CHAR_BUFFER = 03H
  140.  
  141. KEYBRK   PROC     NEAR
  142.  
  143.          MOV      CS:BYTE PTR CHAR_BUFFER,03
  144.          NOP
  145. IRET_INSTRUCTION:
  146.          IRET
  147.  
  148. KEYBRK   ENDP
  149.  
  150. ;        Procedure:        CONIN
  151.  
  152. ;        Purpose: Wait for a character from the console, then return the
  153. ;                           character in [AL].
  154.  
  155. ;        Entry:            No significant registers
  156.  
  157. ;        Exit:             [AL] = Character
  158.  
  159. CONIN_FAR         PROC     FAR
  160. CI1:
  161.          XCHG     DX,AX             ; Restore [AX] from prior save to [DX]
  162.          POP      DX                ; Restore [DX] off stack, drop thru to CONIN
  163.  
  164. CONIN    LABEL    NEAR
  165.  
  166.          MOV      AL,00             ; Prep for exchange with char buffer
  167.          XCHG     AL,CS:BYTE PTR CHAR_BUFFER ; Get char from buffer and clear buffer
  168.          OR       AL,AL             ; Char in buffer ?
  169.          JNZ      CI4               ; Yes, return to caller with char in AL
  170.          PUSH     DX                ; Save [DX] for later restore
  171.          XCHG     DX,AX             ; Save [AX] in [DX] for later [AH] restore
  172.          MOV      AH,00             ; Prep for keyboard I/O
  173.          INT      16H               ; Read next ASCII char struck from keyboard
  174.          OR       AX,AX             ; Error in reading keyboard ?
  175.          JZ       CI1               ; If no char or scan code, try again
  176.          CMP      AX,7200H ; Char need replacement ?
  177.          JNZ      CI2               ; No, skip char replacement
  178.          MOV      AL,10H            ; Replace with Control-P
  179. CI2:
  180.          CMP      AL,00             ; NUL in [AL] ?
  181.          JNZ      CI3               ; No, restore blown registers and exit
  182.                                     ; Yes, put scan code in Char Buffer and
  183.          MOV      CS:BYTE PTR CHAR_BUFFER,AH ;  indicate system or application program
  184. CI3:                                ;  should examine a second code for function
  185.          MOV      AH,DH             ; Restore [AH] from prior save to [DH]
  186.          POP      DX                ; Restore [DX] off stack
  187. CI4:
  188.          RET      
  189.  
  190. CONIN_FAR         ENDP
  191.  
  192. ;        Procedure:        CONOUT
  193.  
  194. ;        Purpose: Output the char in [AL] to the console
  195.  
  196. ;        Entry:            [AL] = char
  197.  
  198. ;        Exit:             No registers may be affected.
  199.  
  200. CONOUT_FAR        PROC     FAR
  201. CONOUT   LABEL    NEAR
  202.  
  203.          PUSH     BP                ; Save registers on the stack
  204.          PUSH     AX
  205.          PUSH     BX
  206.          PUSH     SI
  207.          PUSH     DI
  208.          MOV      AH,0EH            ; Prep for ROM BIOS Call - Write Teletype
  209.          MOV      BX,0007H ;   -set foreground color and alpha mode
  210.          INT      10H               ; Call Video I/O
  211.          POP      DI                ; Restore registers off the stack
  212.          POP      SI
  213.          POP      BX
  214.          POP      AX
  215.          POP      BP
  216.          RET
  217.  
  218. CONOUT_FAR        ENDP
  219.  
  220.  
  221. ;        Procedure:        PRINT
  222.  
  223. ;        Purpose: Output the Char in [AL] to the printer
  224.  
  225. ;        Entry:            [AL] = Char
  226.  
  227. ;        Exit:             No registers may be affected.
  228.  
  229. PRINT_FAR         PROC     FAR
  230. PRINT    LABEL    NEAR
  231.  
  232.          PUSH     AX                ; Save registers on stack
  233.          PUSH     DX
  234.          MOV      CS:BYTE PTR PRINTER_ERROR,00
  235.          NOP
  236. P1:
  237.          MOV      DX,0000           ; Prep for INT, indicate Printer 0
  238.          MOV      AH,00             ; Request - print char in [AL]
  239.          INT      17H               ; Printer I/O
  240.          MOV      DX,OFFSET PAPER_OUT_MSG    ; Default to Paper Out Message
  241.          TEST     AH,20H            ; Out of paper ?
  242.          JNZ      P2                ; Yes, output error message
  243.          MOV      DX,OFFSET PRINTER_FAULT_MSG         ; Default to Printer Fault Message
  244.          TEST     AH,05H            ; Printer Fault ?
  245.          JZ       P3                ; No, continue
  246.          XOR      CS:BYTE PTR PRINTER_ERROR,01
  247.          NOP
  248.          JNZ      P1
  249. P2:
  250.          CALL     PRINT_STRING               ; Output printer error message in DX to console
  251. P3:
  252.          POP      DX                ; Restore registers off stack
  253.          POP      AX
  254.          RET
  255.  
  256. PRINT_FAR         ENDP
  257.  
  258. PRINT_STRING      PROC     NEAR
  259.  
  260.          XCHG     SI,DX             ; [DX] point to string to print 
  261. PS1:                                ; MSG referred to in LODS is dummy
  262.          LODS     CS:BYTE PTR PAPER_OUT_MSG  ; Get byte to output to console
  263.          AND      AL,7FH            ; Clear high bit
  264.          JZ       PS2      ; Exit if we're through
  265.          CALL     CONOUT_FAR        ; Output byte we got from message string
  266.          JMP      PS1      ; Loop until entire message string output
  267.  
  268. PS2:
  269.          XCHG     SI,DX             ; Restore registers to value on entry
  270.          RET
  271.  
  272. PRINT_STRING      ENDP
  273.  
  274.  
  275. ;        Procedure:        AUXIN
  276.  
  277. ;        Purpose: Wait for a byte from the auxiliary input device, then
  278. ;                            return with the byte in [AL]
  279.  
  280. ;        Entry:            No significant registers.
  281.  
  282. ;        Exit:             [AL] = Char, no other registers may be affected
  283.  
  284. AUXIN_FAR         PROC     FAR
  285. AUXIN    LABEL    NEAR
  286.  
  287.          PUSH     DX                ; Save registers on stack
  288.          PUSH     AX
  289.          MOV      DX,0000           ; Prep for RS-232 I/O Interrupt 14H
  290.          MOV      AH,02             ; Receive a byte from Card 0
  291.          INT      14H
  292.          MOV      DX,OFFSET AUX_ERROR_MSG    ; Default to error message
  293.          TEST     AH,0EH            ; Any errors ?
  294.          JZ       AI1               ; No, do normal exit
  295.          CALL     PRINT_STRING      ; Yes, output string pointed to by DX
  296. AI1:
  297.          POP      DX                ; Restore registers
  298.          MOV      AH,DH
  299.          POP      DX
  300.          RET
  301.  
  302. AUXIN_FAR         ENDP
  303.  
  304.  
  305. ;        Procedure:        AUXOUT
  306.  
  307. ;        Purpose: Output the byte in [AL] to the auxiliary output device.
  308.  
  309. ;        Entry:            [AL] = char
  310.  
  311. ;        Exit:             No registers may be affected
  312.  
  313. AUXOUT_FAR        PROC     FAR
  314. AUXOUT   LABEL    NEAR
  315.  
  316.          PUSH     AX                ; Save registers on stack
  317.          PUSH     DX
  318.          MOV      AH,01             ; Prep for RS-232 I/O Interrupt 14H
  319.          MOV      DX,0000           ; Send char in [AL] out Card 0
  320.          INT      14H
  321.          TEST     AH,80H            ; Operation successful ?
  322.          JZ       P3                ; Yes, do normal exit using code in PRINT
  323.          MOV      DX,OFFSET AUX_ERROR_MSG    ; No, point to error message
  324.          JMP      P2       ; Use error exit in AUXIN
  325.  
  326. AUXOUT_FAR        ENDP
  327.  
  328.  
  329. ;        Procedure:        DSKCHG
  330.  
  331. ;        Purpose: Called whenever a directory search has been made and
  332. ;                            disk could have legally been changed to minimize
  333. ;                            unnecessary re-reading of disk directory information
  334. ;                            if the disk has not been changed, and to provide
  335. ;                            configuration information if it has.
  336.  
  337. ;        Entry:            [AL] = Disk Drive #
  338. ;                          [AH] = 0
  339.  
  340. ;        Exit:             [AH] = -1 if disk has been changed
  341. ;                                  0 if it is not known whether disk has been changed
  342. ;                                  1 if disk could not have been changed
  343. ;                          [AL] = I/O driver # to use for this diskette and drive
  344. ;                          Carry Flag = Clear
  345.  
  346. ;                          If DSKCHG requires a disk read and the read fails:
  347. ;                                   Carry Flag = Set
  348. ;                                   [AL] = Error code (See READ or WRITE)
  349.  
  350. DSKCHG_FAR        PROC     FAR
  351. DSKCHG   LABEL    NEAR
  352.  
  353.          SHL      AL,1              ; Only 2 drivers per drive supported
  354.          RET
  355.  
  356. DSKCHG_FAR        ENDP
  357.  
  358.  
  359. ;        Procedure:        SETDATE
  360.  
  361. ;        Purpose: Set system calender
  362.  
  363. ;        Entry:            [AX] = count of days since January 1, 1980
  364.  
  365. ;        Exit:             No significant registers
  366.  
  367. SETDATE_FAR       PROC     FAR
  368. SETDATE  LABEL    NEAR
  369.  
  370.          MOV      CS:WORD PTR DATE_STORAGE,AX         ; Save # of days since Jan 1
  371.          XOR      AX,AX             ; Clear [AX]
  372.          INT      1AH               ; TIME_OF_DAY Interrupt
  373.          RET
  374.  
  375. SETDATE_FAR       ENDP
  376.  
  377.  
  378. ;        Procedure:        SETTIME
  379.  
  380. ;        Purpose: Set system clock
  381.  
  382. ;        Entry:            [CX] and [DX] have the current time
  383. ;                                   [CH] = hours (0 to 23)
  384. ;                                   [CL] = minutes (0 to 59)
  385. ;                                   [DH] = seconds (0 to 59)
  386. ;                                   [DL] = hundredths of seconds (0 to 99)
  387. ;                          Each of these is a binary number that has been
  388. ;                            checked for proper range.
  389.  
  390. ;        Exit:             No significant registers
  391.  
  392. SETTIME_FAR       PROC     FAR
  393. SETTIME  LABEL    NEAR
  394.  
  395.          MOV      AL,60             ; Prep to convert hours to minutes
  396.          MUL      CH                ; [AX] = [CH]*[AL] = Hours*Minutes
  397.          MOV      CH,00
  398.          ADD      AX,CX             ; [AX] = ([CH]*[AL])+[CL] = Total Minutes
  399.          MOV      CX,6000           ; [CX] = # of hundredths of seconds in a minute
  400.          MOV      BX,DX
  401.          MUL      CX                ; Convert time to hundredths of seconds
  402.          MOV      CX,AX
  403.          MOV      AL,100            ; Convert to format required by TIME_OF_DAY
  404.          MUL      BH
  405.          ADD      CX,AX
  406.          ADC      DX,+00
  407.          MOV      BH,00
  408.          ADD      CX,BX
  409.          ADC      DX,00
  410.          XCHG     DX,AX
  411.          XCHG     CX,AX
  412.          MOV      BX,59659
  413.          MUL      BX
  414.          XCHG     DX,CX
  415.          XCHG     DX,AX
  416.          MUL      BX
  417.          ADD      AX,CX
  418.          ADC      DX,00
  419.          XCHG     DX,AX
  420.          MOV      BX,0005
  421.          DIV      BL
  422.          MOV      CL,AL
  423.          MOV      CH,00
  424.          MOV      AL,AH
  425.          CBW
  426.          XCHG     DX,AX
  427.          DIV      BX
  428.          MOV      DX,AX
  429.          MOV      AH,01             ; Count is now in [CX] and [DX]
  430.          INT      1AH               ; Set Time_of_day
  431.          RET
  432.  
  433. SETTIME_FAR       ENDP
  434.  
  435.  
  436. ;        Procedure:        GETDATE
  437.  
  438. ;        Purpose: Read date and time
  439.  
  440. ;        Entry:            No significant registers
  441.  
  442. ;        Exit:             [AX] = count of days since January 1, 1980
  443. ;                          [CH] = hours
  444. ;                          [CL] = minutes
  445. ;                          [DH] = seconds
  446. ;                          [DL] = hundredths of seconds
  447.  
  448. GETDATE_FAR       PROC     FAR
  449. GETDATE  LABEL    NEAR
  450.  
  451.          PUSH     BX                ; Save [BX] on stack
  452.          MOV      AX,00             ; Prep to read TIME_OF_DAY
  453.          INT      1AH               ; Do it
  454.          ADD      CS:WORD PTR DATE_STORAGE,AX         ; Add Current Clock setting to DATE
  455.          MOV      AX,CX             ; Convert TIME_OF_DAY to required format
  456.          MOV      BX,DX
  457.          SHL      DX,1
  458.          RCL      CX,1
  459.          SHL      DX,1
  460.          RCL      CX,1
  461.          ADD      DX,BX
  462.          ADC      AX,CX
  463.          XCHG     DX,AX
  464.          MOV      CX,59659
  465.          DIV      CX
  466.          MOV      BX,AX
  467.          XOR      AX,AX
  468.          DIV      CX
  469.          MOV      DX,BX
  470.          MOV      CX,200
  471.          DIV      CX
  472.          CMP      DL,100
  473.          JC       GD1
  474.          SUB      DL,100
  475. GD1:
  476.          CMC
  477.          MOV      BL,DL
  478.          RCL      AX,1
  479.          MOV      DL,00
  480.          RCL      DX,1
  481.          MOV      CX,60
  482.          DIV      CX
  483.          MOV      BH,DL
  484.          DIV      CL
  485.          XCHG     AL,AH
  486.          MOV      DX,BX
  487.          XCHG     CX,AX
  488.          MOV      AX,CS:WORD PTR DATE_STORAGE
  489.          POP      BX
  490.          RET
  491.  
  492. GETDATE_FAR       ENDP
  493.  
  494.  
  495. ;        Procedure:        FLUSH
  496.  
  497. ;        Purpose: Flush the keyboard buffer
  498.  
  499. ;        Entry:            No significant registers
  500.  
  501. ;        Exit:             Type-ahead buffer is cleared
  502.  
  503.  
  504. FLUSH_FAR         PROC     FAR
  505. FLUSH    LABEL    NEAR
  506.  
  507.          MOV      CS:BYTE PTR CHAR_BUFFER,00 ; Clear char buffer
  508.          NOP
  509.          PUSH     DS                ; Save [DS] on stack
  510.          XOR      BP,BP
  511.          MOV      DS,BP
  512.  
  513.  
  514.          MOV      BUFFER_HEAD,OFFSET KB_BUFFER-400H
  515.          MOV      BUFFER_TAIL,OFFSET KB_BUFFER-400H
  516.  
  517.          POP      DS                ; Restore [DS]
  518.          RET
  519.  
  520. FLUSH_FAR         ENDP
  521.  
  522.  
  523. ;        Procedure:        MAPDEV
  524.  
  525. ;        Purpose: Map physical disk drives with their I/O drivers.
  526.  
  527. ;        Entry:            [AL] = I/O driver used to read the FAT
  528. ;                          [AH] = First byte of FAT (range F8 to FF)
  529.  
  530. ;        Exit:             [AL] = I/O driver for this diskette and drive
  531.  
  532.  
  533. MAPDEV_FAR        PROC     FAR
  534. MAPDEV   LABEL    NEAR
  535.  
  536.          AND      AH,01             ; Test first byte of FAT (FF = 1 = 2-sided)
  537.          OR       AL,AH             ; Add result to even I/O driver # in [AL]
  538.          RET
  539.  
  540. MAPDEV_FAR        ENDP
  541.  
  542.  
  543. ;        Procedure:        MORE_INIT
  544.  
  545. ;        Purpose: Continue initialization process started by INIT
  546.  
  547. ;        Entry:
  548.  
  549. ;        Exit:
  550.  
  551. MORE_INIT_FAR     PROC     FAR
  552. MORE_INIT         LABEL    NEAR
  553.  
  554.  
  555.          XOR      DX,DX             ; Clear [AX] and [DX]
  556.          XOR      AX,AX
  557.          MOV      SS,AX             ; Set stack just below IBMBIO.COM
  558.          MOV      SP,0600H
  559.  
  560.          INT      13H               ; Reset Disk System [AH] = 0
  561.  
  562.          MOV      AL,10100011B      ; Initialize RS-232_I/O
  563.          INT      14H               ;   2400 baud, no parity, 8 bits + 1 stop bit
  564.  
  565.          MOV      AH,01             ; Initialize PRINTER_I/O
  566.          INT      17H
  567.  
  568.          MOV      DS,DX             ; Clear [DS] and [ES] for Int vector patching
  569.          MOV      ES,DX
  570.  
  571.          MOV      AX,0060H ; Set-up segment register address for vector
  572.  
  573.          MOV      WORD PTR KYBD_BRK_VECTOR+2,AX       ; Keyboard break vector
  574.          MOV      WORD PTR KYBD_BRK_VECTOR,OFFSET KEYBRK
  575.  
  576.  
  577.          MOV      DI,0004           ; Patch INT 1 (Single Step) to IRET
  578.          MOV      BX,OFFSET IRET_INSTRUCTION
  579.          XCHG     BX,AX
  580.          STOSW
  581.          XCHG     BX,AX
  582.          STOSW
  583.  
  584.          ADD      DI,+04            ; Patch INT 3 (Break Point) to IRET
  585.          XCHG     BX,AX
  586.          STOSW
  587.          XCHG     BX,AX
  588.          STOSW
  589.  
  590.          XCHG     BX,AX             ; Patch INT 4 (Overflow) to IRET
  591.          STOSW
  592.          XCHG     BX,AX
  593.          STOSW
  594.  
  595.          MOV      WORD PTR STATUS_BYTE,DX    ; Clear memory location 500H (WHY ???)
  596.  
  597.  
  598.          MOV      AX,0BFH           ; Move MS-DOS down to first segment
  599.          MOV      ES,AX             ;   above the I/O System
  600.          MOV      CX,1000H ; Dos length / 2
  601.          CLD
  602.          MOV      AX,0E0H
  603.          MOV      DS,AX
  604.          XOR      DI,DI
  605.          MOV      SI,DI
  606.          REPZ     MOVSW
  607.  
  608.          PUSH     CS                ; Set [DS] = [CS]
  609.          POP      DS
  610.  
  611.          INT      11H               ; Determine equipment attached to system
  612.          ROL      AL,1              ;   specifically # of floppy drives
  613.          ROL      AL,1
  614.          AND      AX,0003           ; Mask in # of floppy drives
  615.          JNZ      SHORT MORONE
  616.  
  617.          ASSUME   DS:CODE
  618.          MOV      BYTE PTR ONE_DRIVE,01      ; Indicate only one floppy
  619.          ASSUME   DS:DATA
  620.  
  621.          INC      AX                ; Extra increment with only one drive
  622. MORONE:
  623.          INC      AX                ; Prep for # of I/O drivers
  624.          SHL      AL,1
  625.          MOV      SI,OFFSET INIT_TABLE
  626.          MOV      [SI],AL           ; Move # of I/O drivers to Init Table
  627.  
  628.          INT      12H               ; Determine memory size and pass to MS-DOS
  629.          MOV      CL,06
  630.          SHL      AX,CL
  631.          XCHG     DX,AX
  632.  
  633.          CALL     DOSSEG_FAR
  634.  
  635.          STI
  636.          XOR      AX,AX             ; Set up INT 25 & 26 (Direct read and write)
  637.          MOV      ES,AX
  638.          MOV      DI,0094H
  639.          MOV      AX,OFFSET DIRECT_READ
  640.          STOSW
  641.          MOV      AX,CS
  642.          STOSW
  643.          MOV      AX,OFFSET DIRECT_WRITE
  644.          STOSW
  645.          MOV      ES:[DI],CS
  646.  
  647.          MOV      DX,100H           ; Set up Disk Transfer Address
  648.          MOV      AH,1AH
  649.          INT      21H
  650.  
  651.          MOV      CX,WORD PTR DS:6  ; Set up data segment and open file
  652.          SUB      CX,0100H
  653.          MOV      BX,DS
  654.          PUSH     CS
  655.          POP      DS
  656.          MOV      DX,OFFSET COMMAND_FCB
  657.          MOV      AH,0FH
  658.          INT      21H
  659.  
  660.          OR       AL,AL             ; Good open ?
  661.          JNZ      BAD_OPERATION
  662.  
  663.          ASSUME   DS:CODE
  664.          MOV      WORD PTR RECORD_LENGTH,0001         ; Force record length to 1
  665.  
  666.  
  667.          MOV      AH,27H            ; and read file
  668.          INT      21H
  669.          
  670.          JCXZ     BAD_OPERATION              ; Good read ?
  671.          CMP      AL,01
  672.          JNZ      BAD_OPERATION
  673.  
  674.          MOV      DS,BX             ; Make all segment registers the same
  675.          MOV      ES,BX
  676.          MOV      SS,BX
  677.  
  678.          MOV      SP,005CH ; Set stack to standard value
  679.  
  680.          XOR      AX,AX             ; Save last DTA for RET L at end of INIT
  681.          PUSH     AX
  682.  
  683.          MOV      DX,0080H ; Set-up new Disk Transfer Address
  684.          MOV      AH,1AH
  685.          INT      21H
  686.  
  687.          PUSH     BX
  688.          MOV      AX,0100H ; Push offset for RET L on stack
  689.          PUSH     AX
  690.          RET
  691.  
  692. MORE_INIT_FAR     ENDP
  693.  
  694. BAD_OPERATION:
  695.          MOV      DX,OFFSET BAD_COMMAND_MSG
  696.          CALL     PRINT_STRING
  697. STALL:   JMP      STALL             ; Hang system
  698.  
  699. COMMAND_FCB:
  700.          DB       1,'COMMAND COM'
  701.          DB       0,0
  702. RECORD_LENGTH:
  703.          DB       23 DUP (0)
  704.  
  705. BAD_COMMAND_MSG:
  706.          DB       13,10,'Bad or missing Command Interpreter',13,10,00
  707.  
  708. INIT_TABLE:
  709.          DB       4                 ; Number of I/O drivers
  710.  
  711.          DB       0                 ; Drive 0
  712.          DW       SDRIVE            ; Single density DPT
  713.          DB       0                 ; Still drive 0
  714.          DW       DDRIVE            ; Double density DPT
  715.  
  716.          DB       1
  717.          DW       SDRIVE
  718.          DB       1
  719.          DW       DDRIVE
  720.  
  721.          DB       2
  722.          DW       SDRIVE
  723.          DB       2
  724.          DW       DDRIVE
  725.  
  726.          DB       3
  727.          DW       SDRIVE
  728.          DB       3
  729.          DW       DDRIVE
  730.  
  731.          DB       0,0,0
  732.  
  733. SDRIVE:
  734.          DW       512               ; SECSIZ - sector size in bytes
  735.          DB       1                 ; CLUSSIZ - # of sectors in allocation unit
  736.          DW       1                 ; RESSEC - # of reserved sectors
  737.          DB       2                 ; FATCNT - # of file allocation tables
  738.          DW       64                ; MAXENT - # of directory entries
  739.          DW       320               ; DSKSIZ - # of physical sectors
  740.  
  741. DDRIVE:
  742.          DW       512
  743.          DB       2
  744.          DW       1
  745.          DB       2
  746.          DW       112
  747.          DW       640
  748.  
  749.          DB       168 DUP (0)
  750.  
  751. DATE_STORAGE:
  752.          DW       0
  753. PRINTER_ERROR:
  754.          DB       0
  755. CHAR_BUFFER:
  756.          DB       0
  757.          DB       0
  758. COMMAND_STORAGE:
  759.          DB       2
  760. VERIFY_FLAG:
  761.          DB       0
  762. IO_DRIVER:
  763.          DB       0
  764. ONE_DRIVE:
  765.          DB       0
  766. SP_STORAGE:
  767.          DW       0
  768. SECTOR_COUNTER:
  769.          DW       0
  770.  
  771.  
  772. ;        Procedure:        READ
  773.  
  774. ;        Purpose: Disk read
  775.  
  776. ;        Entry:            [AL] = I/O driver # (base 0)
  777. ;                          [AH] = Verify flag (write only) 1 = verify after write
  778. ;                          [CX] = # of physical sectors to transfer
  779. ;                          [DX] = logical sector # to start with
  780. ;                          [DS:BX] = Transfer address
  781.  
  782. ;        Exit:             If successful return with Carry Flag clear
  783. ;                          If unsuccessful, Carry Flag Set, CX = sectors remaining
  784. ;                            [AL] = Error Code
  785. ;                               0 = Write protect (Disk writes only)
  786. ;                               2 = Not ready
  787. ;                               4 = Data
  788. ;                               6 = Seek
  789. ;                               8 = Sector not found
  790. ;                              10 = write fault
  791. ;                              12 = Misc. disk error
  792.  
  793.  
  794. DIRECT_READ       LABEL    NEAR
  795.          
  796.          SHL      AL,1              ; Simulate DSKCHG/MAPDEV
  797.  
  798. READ_FAR PROC     FAR
  799. READ     LABEL    NEAR
  800.  
  801.          MOV      AH,02             ; Read command to [AH]
  802.          JMP      SHORT RW_COMMON            ; Join common code
  803.  
  804. SEC_NOT_FND:
  805.          CLC                        ; RET in case of seek error
  806.          MOV      AL,8
  807.          RET      
  808.  
  809. READ_FAR ENDP
  810.  
  811.  
  812. ;        Procedure:        WRITE
  813.  
  814. ;        Purpose: Disk write
  815.  
  816. ;        Entry:            Same as READ
  817.  
  818. ;        Exit:             Same as READ
  819.  
  820.  
  821. DIRECT_WRITE      LABEL    NEAR
  822.  
  823.          SHL      AL,1              ; Simulate DSKCHG/MAPDEV
  824.  
  825. WRITE_FAR         PROC     FAR
  826. WRITE    LABEL    NEAR
  827.  
  828.          MOV      CS:BYTE PTR VERIFY_FLAG,AH 
  829.          MOV      AH,03             ; Write command
  830.  
  831.  
  832. RW_COMMON:
  833.          MOV      CS:BYTE PTR IO_DRIVER,AL   ; Get I/O Driver into [AL]
  834.          SHR      AL,1              ; Convert I/O Driver to Floppy Drive #
  835.          JCXZ     SEC_NOT_FND       ; If # of sectors to transfer is zero, exit
  836.  
  837.          MOV      SI,DX             ; Move # of first sector to transfer to [SI]
  838.          ADD      SI,CX             ;   add # of sectors to transfer
  839.          CMP      SI,641            ; Check for possible overflow 
  840.          CMC                        ; Adjust carry so we'll exit if request is for
  841.          JC       SEC_NOT_FND+1     ;   more than # of sectors on dbl-sided disk
  842.  
  843.          PUSH     ES                ; Save segment registers before saving stack,
  844.          PUSH     DS                ;   so if we error exit out we can recover them
  845.  
  846.          PUSH     DS                ; Set [ES] = [DS]
  847.          POP      ES
  848.  
  849.          PUSH     CS                ; Set [DS] = [CS]
  850.          POP      DS
  851.  
  852.          MOV      WORD PTR SP_STORAGE,SP     ; Now save Stack pointer
  853.          MOV      BYTE PTR COMMAND_STORAGE,AH         ; Save command - READ or WRITE 
  854.  
  855.          CMP      BYTE PTR ONE_DRIVE,1       ; Single drive system ?
  856.          JNZ      RW2               ; No, continue
  857.  
  858.                                     ; Only one drive, get last I/O Driver #
  859.  
  860.          PUSH     DS                ; Save [DS] 
  861.          XOR      SI,SI
  862.          MOV      DS,SI             ; Do XCHG w/ segment = 00
  863.          MOV      AH,AL             ; Move I/O Driver # to [AH]
  864.  
  865.          ASSUME   DS:DATA
  866.          XCHG     AH,BYTE PTR ONE_DRIVE_IO   ; Swap I/O Driver #'s
  867.          ASSUME   DS:CODE
  868.  
  869.          POP      DS                ; Restore [DS]
  870.  
  871.          CMP      AL,AH             ; Same I/O Driver as last disk access ?
  872.          JZ       RW1               ; Yes, skip disk swap pause and prompt
  873.  
  874.          PUSH     DX                ; Save [DX] to restore after prompt sent
  875.          ADD      AL,41H            ; Convert driver # to ASCII letter
  876.          MOV      BYTE PTR DRIVE_LETTER,AL   ; Store drive letter in msg string
  877.          MOV      DX,OFFSET INSERT_MSG       ; Point to msg string
  878.          CALL     PRINT_STRING      ; Output message
  879.          CALL     FLUSH_FAR         ; Clear Keyboard Buffer 
  880.          MOV      AH,00             ; Go wait for keystroke
  881.          INT      16H
  882.          POP      DX                ; Restore [DX]
  883.  
  884. RW1:
  885.          MOV      AL,00             ; Force drive 0 for one drive system
  886.  
  887. RW2:
  888.          XCHG     DX,AX             ; Move first sector to transfer to [AX]
  889.          MOV      DH,08             ; Convert logical sector to track and sector
  890.          DIV      DH                ;   by dividing by number of sectors/track
  891.  
  892.          INC      AH                ; Sectors are numbered 1 to 8 (Not 0 to 7)
  893.  
  894.          MOV      DH,0              ; Set head select byte to Head 0
  895.          TEST     BYTE PTR IO_DRIVER,1       ; Is this a double-sided drive ?
  896.          JZ       RW3               ; No, skip head select adjustment
  897.  
  898.          SHR      AL,1              ; Divide desired track by 2, carry flag is set
  899.          RCL      DH,1              ;   if track is odd - carry flag selects head
  900.  
  901. RW3:
  902.          XCHG     AL,AH             ; [AL] = Sector, [AH] = track
  903.          XCHG     CX,AX             ; [CX] now track/sector, [AX] # of sectors to move
  904.          MOV      WORD PTR SECTOR_COUNTER,AX ; Initialize sector counter
  905.  
  906.          MOV      DI,ES             ; Determine if DMA transfer will cross DMA page
  907.          SHL      DI,1              ;   latch boundary.  [ES] is segment base for
  908.          SHL      DI,1              ;   transfer.  [DI] becomes the least 16 
  909.          SHL      DI,1              ;   significant bits of the 20 bit address
  910.          SHL      DI,1              ;   implied by the [ES] register
  911.          ADD      DI,BX             ; Add Offset base address in [BX] and the length of
  912.          ADD      DI,01FFH ;  one sector to determine maximum transfer address
  913.          JC       RW5               ; Overflow indicates we're beyond DMA page boundary
  914.  
  915.          XCHG     BX,DI             ; No overflow, move maximum transfer address to [BX]
  916.          SHR      BH,1              ; Convert maximum transfer address to # of
  917.          MOV      AH,80H            ;   512 byte sectors we may transfer in [BH]
  918.          SUB      AH,BH             ; Most we can ever transfer is 128, so subtract
  919.          MOV      BX,DI             ;   the maximum allowed by transfer address and
  920.          CMP      AH,AL             ;   compare result to # of sectors to transfer
  921.          JBE      RW4               ; If we're transfering less than maximum, skip
  922.                                     ;   swap
  923.          MOV      AH,AL             ; Transfer maximum, then 
  924. RW4:
  925.          PUSH     AX                ; Save # of sectors to transfer on stack
  926.          MOV      AL,AH             ; [AL] = # of sectors to move on CALL below
  927.          CALL     RW9               ; Do sector transfers
  928.  
  929.          POP      AX                ; [AL] = # of sectors transferred
  930.          SUB      AL,AH             ; Adjust [AH] for sectors just done
  931.          JZ       RW8               ; If zero, we're through, exit
  932.  
  933.                                     ; If we're not through, we're on a DMA latch
  934. RW5:                                ;   page boundary and we need to work through
  935.          DEC      AL                ;   a buffer.  Adjust sector counter for 
  936.          PUSH     AX                ;   buffered operation and save our place
  937.          CLD                        ; Assure forward direction for data moves
  938.          PUSH     BX                ; Save present state of transfer address
  939.          PUSH     ES
  940.          CMP      BYTE PTR COMMAND_STORAGE,2 ; READ operation ?
  941.          JZ       RW6               ; Yes, go read into BIOS buffer
  942.  
  943.          MOV      SI,BX             ; It's a write so we need to fill buffer first
  944.          PUSH     CX                ; [CX] becomes a word counter
  945.          MOV      CX,100H           ; Prep to move one sector (512 bytes)
  946.          PUSH     ES                ; Use segment address in [ES] for write
  947.          POP      DS                ;   to disk buffer
  948.          PUSH     CS
  949.          POP      ES
  950.          MOV      DI,OFFSET MORE_INIT        ; This code is overlayed for a one
  951.          MOV      BX,DI             ;   secotr disk buffer
  952.          REPZ     MOVSW             ; Move sector from DTA to buffer
  953.          POP      CX                ; Restore [CX]
  954.          PUSH     CS                ; Now point [DS] into code segment for move
  955.          POP      DS                ;   from buffer to disk
  956.          CALL     RW13              ; Write one sector out to disk
  957.  
  958.          POP      ES                ; Restore registers
  959.          POP      BX
  960.          JMP      SHORT RW7         ; Skip over buffered sector read code
  961.  
  962. RW6:
  963.          MOV      BX,OFFSET MORE_INIT        ; Read sector into buffer in BIOS
  964.          PUSH     CS
  965.          POP      ES
  966.          CALL     RW13              ; Entrance to disk routine for one sector operation
  967.          
  968.          MOV      SI,BX             ; Prep to move sector from buffer in BIOS
  969.          POP      ES                ;   to requested Disk Transfer Address
  970.          POP      BX
  971.          MOV      DI,BX
  972.          PUSH     CX
  973.          MOV      CX,100H
  974.          REPZ     MOVSW             ; Do it
  975.          POP      CX                ; Restore word counter
  976.  
  977. RW7:
  978.          ADD      BH,02             ; Increment Transfer Address by 512 bytes
  979.          POP      AX                ; 
  980.          CALL     RW9               ; Go do rest of sector transfers, if any
  981. RW8:
  982.          POP      DS                ; Restore segment registers
  983.          POP      ES
  984.          CLC                        ; Indicate successful operation
  985.          RET
  986.  
  987. WRITE_FAR         ENDP
  988.  
  989. RW9_FAR  PROC     FAR               ; Master READ/WRITE routine
  990. RW9      LABEL    NEAR
  991.  
  992.          OR       AL,AL             ; [AL] = # of sectors to transfer
  993.          JZ       RW18              ; If zero, we're done
  994.  
  995.          MOV      AH,09             ; Not done, compute # of sectors left
  996.          SUB      AH,CL             ;   on this track [CL] = physical sector
  997.          CMP      AH,AL             ; Less than a track to go ?
  998.          JBE      RW10              ; Yes, just do what's left of sectors
  999.  
  1000.          MOV      AH,AL             ; More than just this track, do to end of track
  1001. RW10:
  1002.          PUSH     AX                ; Save our place
  1003.          MOV      AL,AH             ; Pass # of tracks to do in [AL]
  1004.          CALL     RW14              ; Enter Do-op routine at retry counter initialization
  1005.          POP      AX                ; Back from sector move, get back where we are
  1006.          SUB      AL,AH             ; Adjust for what we just did
  1007.          SHL      AH,1              ; Multiply # of sectors transferred by 2 to get
  1008.          ADD      BH,AH             ;   affect on DTA address and adjust DTA
  1009.          JMP      RW9               ; Go do some more sectors, if necessary
  1010.  
  1011. RW11:
  1012.          XCHG     DI,AX             ; There's been an error save disk status in [DI]
  1013.          MOV      AH,00             ; Prep for disk reset
  1014.          INT      13H               ; Do it
  1015.  
  1016.          DEC      SI                ; Decrement retry counter
  1017.          JZ       RW12              ; If it's expired, look-up error code and exit
  1018.  
  1019.          MOV      AX,DI             ; Otherwise get back error code in [AH]
  1020.          CMP      AH,80H            ; Is it a write protect error ?
  1021.          JZ       RW12              ; Yes, no need to retry, just error out
  1022.          POP      AX                ; No, get our place back for sector transfers
  1023.          JMP      SHORT RW15        ;   and let's go try again
  1024.  
  1025. RW12:
  1026.          PUSH     CS                ; Error exit
  1027.          POP      ES
  1028.          MOV      AX,DI             ; Prepare to scan error code table
  1029.          MOV      AL,AH
  1030.          MOV      CX,000AH
  1031.          MOV      DI,OFFSET DISK_ERROR_TABLE  
  1032.          REPNZ    SCASB             ; Do it
  1033.          MOV      AL,[DI+9]         ; Use pointer to disk error in [DI] to get DOS error code
  1034.          MOV      CX,WORD PTR SECTOR_COUNTER ; Pass sector counter back to DOS
  1035.          MOV      SP,WORD PTR SP_STORAGE              ; Stack is messed up, restablish to entry condition
  1036.          POP      DS                ; Restore entry segment condition
  1037.          POP      ES
  1038.          STC                        ; Indicate error and exit
  1039.          RET
  1040.  
  1041. RW9_FAR  ENDP
  1042.  
  1043. RW13     PROC     NEAR
  1044.          MOV      AL,1              ; Entry point for buffered operation
  1045. RW14:                               ;   [AL] = one sector only
  1046.          MOV      SI,0005           ; Initial operation entry point, initialize the retry counter
  1047.          MOV      AH,BYTE PTR COMMAND_STORAGE         ; Get desired command out of storage
  1048. RW15:
  1049.          PUSH     AX                ; Retry entry point
  1050.          CMP      CH,40             ; Track > 40 ?
  1051.          JC       RW16              ; No, continue
  1052.  
  1053.          SUB      CH,40             ; Yes, adjust for second side
  1054.          XOR      DH,1              ; Indicate next head
  1055. RW16:
  1056.          INT      13H               ; Do operation in [AH]
  1057.  
  1058.          JC       RW11              ; Carry set indicates error
  1059.  
  1060.          POP      AX                ; [AL] = # of sectors operated on
  1061.          PUSH     AX                ; Re-save on stack
  1062.          CMP      WORD PTR COMMAND_STORAGE,0103H      ; Write cmd w/ verify ?
  1063.          JNZ      RW17              ; No, skip verify
  1064.  
  1065.          MOV      AH,4              ; Yes, do verify [AL] = # of sectors to verify
  1066.          INT      13H
  1067.  
  1068.          JC       RW11              ; Carry set indicates error
  1069.  
  1070. RW17:
  1071.          POP      AX                ; Get # of sectors operated on
  1072.          MOV      AH,0              ; Prep for 16 bit subtract
  1073.          SUB      WORD PTR SECTOR_COUNTER,AX ; Adjust for sectors transferred
  1074.          ADD      CL,AL             ; Add sectors just done to physical sector #
  1075.          CMP      CL,8              ; Have we finished a track ?
  1076. RW18:
  1077.          JBE      RW20              ; No, skip track increment and head toggle
  1078.  
  1079.          MOV      CL,1                       ; Track finished, reset starting sector
  1080.          TEST     BYTE PTR IO_DRIVER,1       ; Double-sided disk ?
  1081.          JZ       RW19              ; No, skip head select
  1082.  
  1083.          XOR      DH,01             ; Double-sided disk, select other head
  1084.          JNZ      RW20              ; If not now head 0, skip track increment
  1085.  
  1086. RW19:
  1087.          INC      CH                ; Increment track
  1088.  
  1089. RW20:
  1090.          RET
  1091.  
  1092. RW13     ENDP
  1093.  
  1094. INSERT_MSG:
  1095.          DB       13,10,'Insert diskette for drive '
  1096. DRIVE_LETTER:
  1097.          DB       'A: and strike'
  1098.          DB       13,10,'any key when ready',13,10,10,00
  1099.  
  1100. DISK_ERROR_TABLE:
  1101.          DB       80H,40H,20H,10H,09,08,04,03,02,01
  1102.  
  1103. DOS_ERROR_CODES:
  1104.          DB       02H,06,0CH,04,0CH,04,08,00,0CH,0CH
  1105.  
  1106.          DB       1DH,3CH,00,74H,1DH,8AH,0EH,7FH,1DH,0F6H,0D1H,0B4H,00,89H
  1107.  
  1108.          DB       0C3H,8AH,87H,0C9H,32H,3AH,06,72H,1DH,0B0H,0FFH,77H,01
  1109.          DB       40H,22H,0C1H,0D0H,0D8H
  1110.  
  1111.          DB       80H,40H,20H,10H,09,08,04,03,02,01
  1112.          DB       02,06,0CH,04,0CH,04,08,00,0CH,0CH
  1113.  
  1114.          DB       ' ready',13,10,10,00
  1115.  
  1116.          DB       80H,40H,20H,10H,09,08,04,03,02,01
  1117.          DB       02,06,0CH,04,0CH,04,08,00,0CH,0CH
  1118.  
  1119.          DB       29 DUP (0)
  1120.  
  1121.  
  1122.  
  1123. ;        Procedure:        INIT
  1124.  
  1125. ;        Purpose: System initialization
  1126.  
  1127. ;        Entry:            Established by bootstrap loader
  1128.  
  1129.  
  1130. ;        Exit:             Jump to 100H in new program segment
  1131.  
  1132.  
  1133. INIT_FAR PROC     FAR
  1134. INIT     LABEL    NEAR
  1135.  
  1136.          PUSH     SI                ; Save registers on stack
  1137.          PUSH     DI
  1138.          PUSH     DS
  1139.          PUSH     ES
  1140.          PUSH     CX
  1141.          PUSH     AX
  1142.  
  1143.          XOR      AX,AX             ; Set-up [DS] and [ES] for block move
  1144.          MOV      ES,AX
  1145.          PUSH     CS
  1146.          POP      DS
  1147.          MOV      SI,OFFSET DSKTBL  ; Point to disk parameter table at end of code
  1148.          MOV      DI,0570H ; Point to BIOS RAM Area
  1149.          MOV      AX,DI             ; Save pointer in [AX]
  1150.          MOV      CX,11             ; Prep to move 11 bytes
  1151.          REPZ     MOVSB             ; Do it
  1152.          MOV      DI,0078H ; Point to INT 1EH, pointer to dsk parm table
  1153.          STOSW                      ; Store offset address of BIOS RAM area table
  1154.          XOR      AX,AX             ; Set segment address for interrupt to 00
  1155.          STOSW
  1156.  
  1157.          POP      AX                ; Restore registers off stack
  1158.          POP      CX
  1159.          POP      ES
  1160.          POP      DS
  1161.          POP      DI
  1162.          POP      SI
  1163.  
  1164.          JMP      MORE_INIT         ; Continue init elsewhere
  1165.  
  1166. INIT_FAR ENDP
  1167.  
  1168. DSKTBL:
  1169.          DB       11011111B         ; SRT=D, HD UNLOAD = 0F - 1st specify byte
  1170.          DB       00000010B         ; HD LOAD = 1, MODE = DMA - 2nd specify byte
  1171.          DB       25H               ; Wait after OPN til motor off
  1172.          DB       2                 ; 512 bytes/sector
  1173.          DB       8                 ; EOT (last sector on track)
  1174.          DB       2AH               ; Gap length
  1175.          DB       0FFH              ; DTL
  1176.          DB       50H               ; Gap length for format
  1177.          DB       0F6H              ; Fill byte for format
  1178.          DB       00                ; Head settle time (milliseconds)
  1179.          DB       4                 ; Motor start time (1/8 seconds)
  1180.  
  1181.          DB       0E9H,0C9H,0FFH    ; Un-explained bytes
  1182.  
  1183.          DB       179H DUP (0)
  1184.  
  1185. CODE     ENDS
  1186.  
  1187.          END      BIOS
  1188.